package com.baofu.paycore.biz.tools.aspect;

import com.baofu.account.service.facade.model.result.AccountBaseResDTO;
import com.baofu.paycore.biz.tools.AssertResponse;
import com.baofu.paycore.biz.tools.BuildModel;
import com.baofu.paycore.common.constant.PayCoreConstant;
import com.baofu.paycore.common.enums.BizCmdTypeEnum;
import com.baofu.paycore.common.exception.PayCoreBizException;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.common.utils.DateUtil;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.google.common.base.Objects;
import com.system.commons.result.Result;
import static com.baofu.paycore.biz.tools.RetrySet.*;

import com.system.dispatch.dal.mapper.BizCmdMapper;
import com.system.dispatch.dal.model.BizCmdDO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;

/**
 * 账务测重试
 *
 * <p>
 *  1.环绕通知
 * </p>
 * User: xin deng  Date: 16/7/5 ProjectName: paycore Version: 1.0
 */
@Slf4j
@Aspect
@Component
public class AccRetryAspect {

    /**
     * 业务指令mapper
     */
    @Autowired
    private BizCmdMapper bizCmdMapper;

    @Value("${accRetry.next.exeTime}")
    private Integer nextExeTime;

    /**
     * 环绕通知
     *
     * @param pj    切点
     * @return      结果
     */
    @Around(value="execution(com.system.commons.result.Result com.baofu.paycore.biz.external.account.AccountRetryService.*(" +
            "java.lang.String,java.lang.String,*))")
    public Result around(ProceedingJoinPoint pj) {
        Result<AccountBaseResDTO> response = null;
        try {
            String payNo = (String) pj.getArgs()[0];
            String requestType = (String) pj.getArgs()[1];
            response = (Result<AccountBaseResDTO>) pj.proceed();
            String method = pj.getSignature().getName();
            log.debug("方法：{}、远程调用 params:{},{},响应：{}", method, payNo, requestType, response);

            if (!validateTryAble(requestType, response, method)) {
                return response;
            }
            String bizId = requestType.concat(PayCoreConstant.BASE_CMD_SEPARATOR).concat(payNo);
            //插入dispatch重试
            List<BizCmdDO> bizCmdDOs = bizCmdMapper.selectByBizIdAndType(bizId, BizCmdTypeEnum.ACC_RETRY.getCode());
            if (bizCmdDOs != null && bizCmdDOs.size() > 0) {
                log.debug("命令已插入过，不能重复插入：{}", bizId);
                return new Result<>(PayCoreErrorCode.ACCEPT_RESPONSE.getErrorCode(), response.getErrorMsg());
            }
            int n = bizCmdMapper.insert(BuildModel.buildBizCmd(bizId, BizCmdTypeEnum.ACC_RETRY.getCode(),
                    DateUtil.addSeconds(new Date(), nextExeTime), 3, null));
            log.info("账务侧重试插入任务数:{},{}", n, requestType);
            response = new Result<>(PayCoreErrorCode.ACCEPT_RESPONSE.getErrorCode(), response.getErrorMsg());
        } catch (Throwable throwable) {
            log.error("切面响应,重试异常：{}", response,throwable);
        }
        return response;
    }

    /**
     * 校验是否可以发起重试
     * @param requestType       当前交易的服务类型
     * @param response          错误码
     * @return                  是否可以重试
     *                              true  可以重试
     *                              false 不饿能够重试
     */
    private boolean validateTryAble(String requestType, Result<AccountBaseResDTO> response,String method) {
        if (response == null) {
            log.debug("远程调用响应为空:{}",requestType);
            throw new PayCoreBizException(PayCoreErrorCode.REMOTE_SERVICE_INVOKE_EXCEPTION);
        }

        if(response.isSuccess() && response.getResult() != null && this.checkAccountStatus(response, method)){
            log.debug("状态初始重试");
            return true;
        }
        boolean flag = retrySet.contains(requestType) ;
        if (flag && Objects.equal(response.getErrorCode(), PayCoreErrorCode.REMOTE_SERVICE_TIME_OUT.getErrorCode())) {
            log.debug("需重试的业务，远程调用超时，重试 requestType:{}", requestType);
            return true;
        }
        if (flag && Objects.equal(response.getErrorCode(),
                PayCoreErrorCode.REMOTE_SERVICE_INVOKE_EXCEPTION.getErrorCode())) {
            log.debug("需重试的业务，远程服务调用返回异常，重试 requestType:{}", requestType);
            return true;
        }
//        if (AccErrorCode.from(response.getErrorCode())) {
//            log.debug("特定错误需要重试，重试 errCode:{}", response.getErrorCode());
//            return true;
//        }
        log.debug("其他，不重试{},响应：{}", requestType, response);
        return false;
    }

    /**
     * 判断账务返回状态是否需重试
     *
     * @param response      响应对象
     * @param method        调用账务的方法
     * @return              是否需重试
     */
    private boolean checkAccountStatus(Result<AccountBaseResDTO> response, String method){

        //账务返回状态
        PayStatusEnums status = AssertResponse.assertAccountSuccess(response);
        log.debug("调用账务的方法:{}, 账务返回状态:{}", method, status.getCode());

        switch (status){
            case INIT:
                return true;
            default:
                return false;
        }
    }
}
